﻿/*
 * Created by SharpDevelop.
 * User: Vendan
 * Date: 3/7/2012
 * Time: 12:38 AM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Net;
using System.Collections.Generic;
using Un4seen.Bass;
using System.Threading;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace GMusic
{
	/// <summary>
	/// Description of GMusicPlayer.
	/// </summary>
	
	public delegate void delSongStart(string name, string artist, string album);
	public delegate void delSongEnd();
	
	public class GMusicPlayer
	{
		int currentsong = -1;
		int cursync = -1;
		public Dictionary<string, Dictionary<string, Dictionary<int, Tuple<string, string>>>> tracks = new Dictionary<string, Dictionary<string, Dictionary<int, Tuple<string, string>>>>();
		string xt;
		CookieContainer cookies = new CookieContainer();
		
		bool albumplaying = false;
		string albumartist;
		string albumname;
		int albumtrack;
		
		bool playing = false;
		
		public event delSongStart SongStart;
		public event delSongEnd SongEnd;
		
		public delegate void onTrackEnd(int handle, int channel, int data, int user);
		
		public GMusicPlayer()
		{
			Bass.BASS_Init(-1, 44100, 0, IntPtr.Zero);
		}
		
		public void Login(string account, string password)
		{
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://accounts.google.com/ServiceLogin?service=sj");
			request.CookieContainer = cookies;
			HttpWebResponse response = (HttpWebResponse)request.GetResponse();
			string output = (new StreamReader(response.GetResponseStream()).ReadToEnd());
			Match dsh = Regex.Match(output, "id=\"dsh\"\\s*value=\"([^\"]*)\"");
			Match galx = Regex.Match(output, "name=\"GALX\"\\s*value=\"([^\"]*)\"");
			
			request = (HttpWebRequest)WebRequest.Create("https://accounts.google.com/ServiceLoginAuth");
			request.CookieContainer = cookies;
			request.Method = WebRequestMethods.Http.Post;
			request.ContentType = "application/x-www-form-urlencoded";
			byte[] login = Encoding.ASCII.GetBytes("Email=" + account + "&Passwd=" + password + "&dsh="+dsh.Groups[1]+"&GALX="+galx.Groups[1]);
			request.ContentLength = login.Length;
			Stream dataStream = request.GetRequestStream();
			dataStream.Write(login, 0, login.Length);
			dataStream.Close();
			response = (HttpWebResponse)request.GetResponse();
			request = (HttpWebRequest)WebRequest.Create("https://play.google.com/music/listen?u=0");
			request.CookieContainer = cookies;
			response = (HttpWebResponse)request.GetResponse();
			Uri googleplay = new Uri("https://play.google.com");
			if(cookies.GetCookies(new Uri("https://play.google.com/music/listen")) == null || cookies.GetCookies(new Uri("https://play.google.com/music/listen"))["xt"] == null)
			{
				throw new Exception("Invalid Login");
			}
			else
			{
				xt = cookies.GetCookies(new Uri("https://play.google.com/music/listen"))["xt"].Value;
			}
		}
		
		public void Logout()
		{
			tracks = new Dictionary<string, Dictionary<string, Dictionary<int, Tuple<string, string>>>>();
			cookies = new CookieContainer();
			xt = "";
		}
		
		public void GetTracks()
		{
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://play.google.com/music/services/loadalltracks?u=0&xt=" + xt);
			request.CookieContainer = cookies;
			request.Method = WebRequestMethods.Http.Post;
			request.ContentType = "application/x-www-form-urlencoded";
			byte[] json = Encoding.ASCII.GetBytes("json=%7B%7D");
			request.ContentLength = json.Length;
			Stream dataStream = request.GetRequestStream();
			dataStream.Write(json, 0, json.Length);
			dataStream.Close();
			HttpWebResponse response = (HttpWebResponse)request.GetResponse();
			string output = (new StreamReader(response.GetResponseStream()).ReadToEnd());
			JObject info = JObject.Parse(output);
			foreach (JObject track in info["playlist"])
			{
				if(!tracks.ContainsKey((string)track["artist"]))
				{
					tracks.Add((string)track["artist"], new Dictionary<string, Dictionary<int, Tuple<string,string>>>());
				}
				if(!tracks[(string)track["artist"]].ContainsKey((string)track["album"]))
				{
					tracks[(string)track["artist"]].Add((string)track["album"], new Dictionary<int, Tuple<string, string>>());
				}
				if(!tracks[(string)track["artist"]][(string)track["album"]].ContainsKey((int)track["track"]))
				{
					tracks[(string)track["artist"]][(string)track["album"]].Add((int)track["track"], new Tuple<string, string>((string)track["title"], (string)track["id"]));
				}
			}
		}
		
		public void playSong(string artist, string album, int track)
		{
			if(currentsong!=-1)
			{
				Bass.BASS_ChannelStop(currentsong);
				Bass.BASS_StreamFree(currentsong);
			}
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://play.google.com/music/play?u=0&songid=" + tracks[artist][album][track].Item2 + "&pt=e");
			request.CookieContainer = cookies;
			HttpWebResponse response = (HttpWebResponse)request.GetResponse();
			string output = (new StreamReader(response.GetResponseStream()).ReadToEnd());
			JObject info = JObject.Parse(output);
			currentsong = Bass.BASS_StreamCreateURL((string)info["url"], 0, 0, null, IntPtr.Zero);
			Bass.BASS_ChannelPlay(currentsong, false);
			SongStart(tracks[artist][album][track].Item1, artist, album);
			playing = true;
		}
		
		public void playPause()
		{
			if(currentsong != -1)
			{
				if(playing)
				{
					Bass.BASS_ChannelPause(currentsong);
				}
				else
				{
					Bass.BASS_ChannelPlay(currentsong, false);
				}
				playing = !playing;
			}
		}
		
		public void stop()
		{
			Bass.BASS_ChannelStop(currentsong);
			Bass.BASS_StreamFree(currentsong);
			albumplaying = false;
			currentsong = -1;
			SongEnd();
		}
		
		public void onTrackEndAlbum(int handle, int channel, int data, IntPtr user)
		{
			albumtrack++;
			bool foundtrack = false;
			int newtrack = int.MaxValue;
			foreach(int key in tracks[albumartist][albumname].Keys)
			{
				if(key >= albumtrack && key < newtrack)
				{
					foundtrack = true;
					newtrack = key;
				}
			}
			if(!foundtrack)
			{
				albumplaying = false;
				currentsong = -1;
				return;
			}
			albumtrack = newtrack;
			SongEnd();
			playSong(albumartist, albumname, newtrack);
			cursync = Bass.BASS_ChannelSetSync(currentsong, BASSSync.BASS_SYNC_END, 0, new SYNCPROC(this.onTrackEndAlbum), IntPtr.Zero);
		}
		
		public void playAlbum(string artist, string album)
		{
			if(albumplaying && cursync != -1 && currentsong != -1)
			{
				Bass.BASS_ChannelRemoveSync(currentsong, cursync);
				cursync = -1;
			}
			albumartist = artist;
			albumname = album;
			albumtrack = 0;
			albumplaying = true;
			onTrackEndAlbum(0, 0, 0, IntPtr.Zero);
		}
	}
}
